package com.gframework.autoconfigure.thread;

import java.util.concurrent.Future;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import com.gframework.autoconfigure.thread.AsyncConfig.AsyncProperties;

/**
 * 配置spring异步任务线程池.
 * <p>异步任务指的就是{@link Async}注解声明的方法，但不排除spring其他地方也会用到此线程池。
 * <p>此线程池不易过大或过小，并且需要小心使用，否则会产生极其严重的OOM或死锁问题。
 * <ul>
 * <li><strong>死锁：</strong>如果异步任务过多而且存在嵌套，那么当就可能会出现两个异步任务在申请新异步方法的时候，线程池满了，互相进入堵塞状态，这就导致了死锁。</li>
 * <li><strong>OOM：</strong>异步任务执行时间不宜过长，如果过长，在高并发情况下会导致线程堆积越来越多，如果没有设置合理的上线，就会导致OOM</li>
 * <li><strong>异常：</strong>异步方法调用的时候有两个异常，一个是方法内部异常，这个异常只有在通过{@link Future#get()}方法获取结果的时候出现。另一个异常是在调用异步方法的时候出现，这通常是因为线程池满了，并且堵塞队列也满了。因此线程池不易频繁滥用</li>
 * <li><strong>持久化队列：</strong>如果对非查询的异步队列需求量过高，或者执行时间可能很长，可以考虑持久化队列如MQ，但是也要考虑执行性能，因为MQ也是会出现队列堆积过多导致的OOM。适当的增加消费者或许可以更好地解决这个问题，因此队列监控就显得非常重要。</li>
 * </ul>
 * 
 * @since 1.0.0
 * @author Ghwolf
 * 
 * @see ThreadPoolTaskExecutor
 */
@Configuration(proxyBeanMethods=false)
@EnableConfigurationProperties(AsyncProperties.class)
public class AsyncConfig {
	private static final Logger logger = LoggerFactory.getLogger(AsyncConfig.class);
	
	@Autowired
	private AsyncProperties asyncProperties;
	
	@Bean("asyncTaskThreadPool")
	public ThreadPoolTaskExecutor getAsyncTaskThreadPool() {
		ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
		pool.setCorePoolSize(asyncProperties.corePoolSize);
		pool.setMaxPoolSize(asyncProperties.maxPoolSize);
		pool.setQueueCapacity(asyncProperties.queueCapacity);
		pool.setKeepAliveSeconds(asyncProperties.keepAliveSeconds);
		pool.setDaemon(asyncProperties.daemon);
		pool.setThreadPriority(asyncProperties.threadPriority);
		// TODO 异常处理器待完善
		logger.info("\nspring异步线程池创建：线程池核心线程数：{}，最大值：{}，队列大小：{}，最大空闲时间：{}，{}守护线程，优先级：{}。",
				asyncProperties.corePoolSize,
				asyncProperties.maxPoolSize,
				asyncProperties.queueCapacity,
				asyncProperties.keepAliveSeconds,
				asyncProperties.daemon ? "是" : "不是",
				asyncProperties.threadPriority);
		return pool;
	}

	@ConfigurationProperties("gframework.spring.async-thread-pool")
	static class AsyncProperties {
		/**
		 * 核心线程数（初始化线程数），默认3
		 */
		private int corePoolSize = 3;
		
		/**
		 * 线程池最大值，超过了会在等待队列堵塞，默认100
		 */
		private int maxPoolSize = 100;
		
		/**
		 * 等待队列最大值，超过了则后续调用方法会出现异常，默认5000
		 */
		private int queueCapacity = 5000;
		
		/**
		 * 空闲线程最大存活时间（秒），默认60
		 */
		private int keepAliveSeconds = 60;
		
		/**
		 * 是否守护线程，默认true
		 */
		private boolean daemon = true;
		
		/**
		 * 线程优先级，1~10，默认5
		 */
		private int threadPriority = 5;
		
		public int getCorePoolSize() {
			return this.corePoolSize;
		}

		public void setCorePoolSize(int corePoolSize) {
			this.corePoolSize = corePoolSize;
		}

		public int getMaxPoolSize() {
			return this.maxPoolSize;
		}

		public void setMaxPoolSize(int maxPoolSize) {
			this.maxPoolSize = maxPoolSize;
		}

		public int getQueueCapacity() {
			return this.queueCapacity;
		}

		public void setQueueCapacity(int queueCapacity) {
			this.queueCapacity = queueCapacity;
		}

		public int getKeepAliveSeconds() {
			return this.keepAliveSeconds;
		}

		public void setKeepAliveSeconds(int keepAliveSeconds) {
			this.keepAliveSeconds = keepAliveSeconds;
		}

		public boolean isDaemon() {
			return this.daemon;
		}

		public void setDaemon(boolean daemon) {
			this.daemon = daemon;
		}

		public int getThreadPriority() {
			return this.threadPriority;
		}

		public void setThreadPriority(int threadPriority) {
			if (threadPriority > 10 || threadPriority < 1) {
				logger.warn("spring.async-thread-pool.thread-priority 线程优先级只能是1~10，但是设置的却是：{}，设置不生效，采用默认的5。",threadPriority);
				return ;
			}
			this.threadPriority = threadPriority;
		}
	}
	
}
